#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UTable.ObjectManager.Commands;
using UTable.Objects.Policies;
using UTable.Input;
using System.Windows;
using UTable.Input.MultiTouch;
using System.Collections.ObjectModel;

namespace UTable.Objects
{
    public class ULocalObjectManager: ILocalObjectManager
    {
        private AuthorizationPolicy _authorizationPolicy;

        private ConflictResolvePolicy _conflictResolvePolicy;

        private LayoutPolicy _layoutPolicy;

        private IObject _target;

        #region Privte Methods

        private MultiTouchEventArgs ExtractCapturedFingers(MultiTouchEventArgs args)
        {
            if (args == null)
                return null;
            Collection<FingerEventArgs> fingers = new Collection<FingerEventArgs>();
            foreach (FingerEventArgs finger in args.FingerEvents)
            {
                if (finger.Captured)
                {
                    fingers.Add(finger);
                }
            }
            if (fingers.Count > 0)
            {
                foreach (FingerEventArgs finger in fingers)
                {
                    args.FingerEvents.Remove(finger);
                }
                return new MultiTouchEventArgs(fingers, args.Timestamp);
            }
            return null;
        }

        private void PreProcessMultiTouchInputEvent(IObject receiver, MultiTouchEventArgs args)
        {
            if (args == null)
                return;
            MultiTouchEventArgs captured = ExtractCapturedFingers(args);
            if (captured != null)
            {
                this.ExecuateCommand(new ObjectCommand(receiver, ObjectCommandType.NotMappedInput, captured));
            }
        }

        #endregion


        #region ILocalObjectManager Members

        public IObject Target
        {
            get
            {
                return _target;
            }
            set
            {
                _target = value;
            }
        }

        public AuthorizationPolicy AuthorizationPolicy
        {
            get
            {
                return _authorizationPolicy;
            }
            set
            {
                _authorizationPolicy = value;
                // _authorizationPolicy.Target = Target;
            }
        }

        public ConflictResolvePolicy ConflictResolvePolicy
        {
            get
            {
                return _conflictResolvePolicy;
            }
            set
            {
                _conflictResolvePolicy = value;
                // _conflictResolvePolicy.Target = Target;
            }
        }

        public LayoutPolicy LayoutPolicy
        {
            get
            {
                return _layoutPolicy;
            }
            set
            {
                _layoutPolicy = value;
                // _layoutPolicy.Target = Target;
            }
        }


        public void AuthorizeCommands(List<ICommand> cmds)
        {
            if (_authorizationPolicy != null)
                _authorizationPolicy.AuthorizeCommands(cmds);
        }

        public void ResolveCommandsConflict(List<ICommand> cmds)
        {
            if (_conflictResolvePolicy != null)
                _conflictResolvePolicy.ResolveCommandsConflict(cmds);
        }

        public void ExecuateCommand(ObjectCommand cmd)
        {
            if (this.LayoutPolicy == null)
                return;
            switch (cmd.SubType)
            {
                case ObjectCommandType.Input:
                    // firstly test whether this input event is mapped on the content
                    // if so just send the input event to the content
                    // otherwise, send the input event to the layout policy and raise the input event

                    // However, the procedure above cannot execute in sequence
                    // Thus I split the procedure into two commands: input and mapped input
                    // for input command, we just send it to the object's content thread, and let it do the hit test
                    // if the input does not hit the content, it will send the NotMapped input to the object, 
                    // and then the local manager can put the input the layout policy

                    // do some tricks for multi-touch event
                    if (cmd.Param is MultiTouchEventArgs)
                    {
                        PreProcessMultiTouchInputEvent(cmd.Receiver, cmd.Param as MultiTouchEventArgs);
                        if ((cmd.Param as MultiTouchEventArgs).FingerEvents.Count == 0)
                            return;
                    }
                    if (UTableHelper.IsObjectWaiting(cmd.Receiver))
                    {
                        return;
                    }
                    (cmd.Receiver as IObjectEntity).ExecuateCommand(new ContentCommand(cmd.Receiver, ContentCommandType.Input, cmd.Param));
                    break;
                case ObjectCommandType.NotMappedInput:
                    cmd.Receiver.RaiseEvent((cmd as ObjectCommand).Param as InputEventArgs);
                    LayoutPolicy.OnInputReceived(cmd.Receiver, cmd.Param as InputEventArgs);
                    break;
                case ObjectCommandType.Insert:
                    LayoutPolicy.OnObjectInserted(cmd.Param as IObject);
                    break;
                case ObjectCommandType.Remove:
                    LayoutPolicy.OnObjectRemoved(cmd.Param as IObject);
                    break;
                case ObjectCommandType.Close:
                    LayoutPolicy.OnObjectClosed(cmd.Param as IObject);
                    break;
                case ObjectCommandType.Join:
                    LayoutPolicy.JoinObjects(cmd.Param as ObjectJoinCommandArgs);
                    break;
                case ObjectCommandType.Translate:
                    LayoutPolicy.TranslateObject(cmd.Receiver, (Point)cmd.Param);
                    break;
                case ObjectCommandType.Rotate:
                    LayoutPolicy.RotateObject(cmd.Receiver, (Double)cmd.Param);
                    break;
                case ObjectCommandType.Scale:
                    LayoutPolicy.ScaleObject(cmd.Receiver, (Size)cmd.Param);
                    break;
                case ObjectCommandType.Resize:
                    LayoutPolicy.ResizeObject(cmd.Receiver, (Size)cmd.Param);
                    break;
                case ObjectCommandType.Maximize:
                    LayoutPolicy.MaximizeObject(cmd.Receiver);
                    break;
                case ObjectCommandType.Minimize:
                    LayoutPolicy.MinimizeObject(cmd.Receiver);
                    break;
                case ObjectCommandType.Restore:
                    LayoutPolicy.RestoreObject(cmd.Receiver);
                    break;
                case ObjectCommandType.Copy:
                    LayoutPolicy.CopyObject(cmd.Receiver);
                    break;
                case ObjectCommandType.ChangeLayoutState:
                    LayoutPolicy.ChangeLayoutState(cmd.Receiver, (ObjectLayoutState)cmd.Param);
                    break;
                case ObjectCommandType.ChangeOperatingState:
                    LayoutPolicy.ChangeOperatingState(cmd.Receiver, (ObjectOperatingState)cmd.Param);
                    break;
                case ObjectCommandType.ChangeVisibility:
                    LayoutPolicy.ChangeVisibility(cmd.Receiver, (Visibility)cmd.Param);
                    break;
                case ObjectCommandType.ChangeZIndex:
                    LayoutPolicy.ChangeZIndex(cmd.Receiver, (int)cmd.Param);
                    break;
                case ObjectCommandType.ChangeClip:
                    LayoutPolicy.ChangeClip(cmd.Receiver, (System.Windows.Media.Geometry)cmd.Param);
                    break;

            }
        }

        #endregion
    }
}
